package de.tu_dortmund.inf.vuc.mgpl.validation;

import java.util.Iterator;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;

import de.tu_dortmund.inf.vuc.mgpl.mPGL.AssStmt;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.Block;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.Decl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.GameAttrAss;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.GameAttrAssList;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.MPGLPackage;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.Stat;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.Var;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.mPGL;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.AnimBlockImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.CircleAnimationDeclImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.CircleVarArrayImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.CircleVarImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.EdgedVarArrayImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.EdgedVarImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.ExpressionImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.RectangleAnimationDeclImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.TouchedVarImpl;
import de.tu_dortmund.inf.vuc.mgpl.mPGL.impl.TriangleAnimationDeclImpl;

public class MPGLJavaValidator extends AbstractMPGLJavaValidator {

	/**
	 * the error message to display
	 */
	private String errorMessage = "";
	/**
	 * error found helper variable
	 */
	private boolean errorFound = false;

	/**
	 * check the type of the assignment at both sides (static type-check)
	 * 
	 * @param attrAss
	 *            the attrAss to check
	 */
	// TODO: Reprogramm
	@Check(CheckType.FAST)
	public void checkAssignmentVariableType(AssStmt assStmt) {
		Var var = assStmt.getVar(); // Left
		Stat stat = assStmt.getAssignStat(); // Right

		if (var.getVar() instanceof EdgedVarArrayImpl) {
			EdgedVarArrayImpl node = (EdgedVarArrayImpl) var.getVar();
			if (node.getName().equals("animation_block")) {
				EObject obj = getRightAssigmentSide(stat);
				if(!(obj instanceof RectangleAnimationDeclImpl) && !(obj instanceof TriangleAnimationDeclImpl)) {
					System.out.println("need to be edged, but is " + obj);
					error("right side need to be a from the same animation type", MPGLPackage.MPGL__NAME);
				}
			} 
		} else if (var.getVar() instanceof EdgedVarImpl) {
			EdgedVarImpl node = (EdgedVarImpl) var.getVar();
			if (node.getName().equals("animation_block")) {
				EObject obj = getRightAssigmentSide(stat);
				if(!(obj instanceof RectangleAnimationDeclImpl) && !(obj instanceof TriangleAnimationDeclImpl)) {
					System.out.println("need to be edged, but is " + obj);
					error("right side need to be a from the same animation type", MPGLPackage.MPGL__NAME);
				}
			}
		} else if (var.getVar() instanceof CircleVarArrayImpl) {
			CircleVarArrayImpl node = (CircleVarArrayImpl) var.getVar();
			if (node.getName().equals("animation_block")) {
				EObject obj = getRightAssigmentSide(stat);
				if(!(obj instanceof CircleAnimationDeclImpl)) {
					System.out.println("need to be circle, but is " + obj);
					error("right side need to be a circle animation type", MPGLPackage.MPGL__NAME);
				}
			}
		} else if (var.getVar() instanceof CircleVarImpl) {
			CircleVarImpl node = (CircleVarImpl) var.getVar();
			if (node.getName().equals("animation_block")) {
				EObject obj = getRightAssigmentSide(stat);
				if(!(obj instanceof CircleAnimationDeclImpl)) {
					System.out.println("need to be circle, but is " + obj);
					error("right side need to be a circle animation type", MPGLPackage.MPGL__NAME);
				}
			}
		}
	}

	private EObject getRightAssigmentSide(Stat stat) {
		if (stat.getExpr() instanceof ExpressionImpl) {
			ExpressionImpl expr = (ExpressionImpl) stat.getExpr();
			TouchedVarImpl touched = (TouchedVarImpl) expr.getAtom();
			EdgedVarImpl edged = (EdgedVarImpl) touched.getVar().getVar();
			AnimBlockImpl animBlock = (AnimBlockImpl) edged.getRef();
			EObject type = animBlock.getAnimationType().getType();
			return type;
		}
		return null;
	}

	/**
	 * check if every attributes occurs only one time at the game attr list
	 * 
	 * @param gameAttrAss
	 *            the game attr list to check
	 */
	@Check(CheckType.FAST)
	public void checkGameAttrUniqOccurence(GameAttrAss gameAttrAss) {
		try {
			GameAttrAssList list = (GameAttrAssList) gameAttrAss.eContainer();
			TreeIterator<EObject> it = list.eAllContents();
			while (it.hasNext()) {
				GameAttrAss element = (GameAttrAss) it.next();
				if (element != gameAttrAss
						&& element.getName().equals(gameAttrAss.getName())) {
					error("" + gameAttrAss.getName()
							+ " is not uniq in the game attribute list",
							MPGLPackage.MPGL__NAME);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * check if every attributes occurs only one time at the attr list
	 * 
	 * @param attrAss
	 *            the attr list to check
	 */
	// TODO: Reprogramm
	// @Check(CheckType.FAST)
	// public void checkAttrUniqOccurence(AttrAss attrAss) {
	// try {
	// AttrAssList list = (AttrAssList) attrAss.eContainer();
	// Iterator<AttrAss> it = list.getAttr().iterator();
	// while (it.hasNext()) {
	// AttrAss element = (AttrAss) it.next();
	//
	// // TODO: Check here against new gramma
	//
	// // if (element != attrAss
	// // && element.getName().equals(attrAss.getName())) {
	// // error("" + attrAss.getName()
	// // + " is not uniq in the attribute list",
	// // MPGLPackage.MPGL__NAME);
	// // }
	// }
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	// }

	/**
	 * check if the name of the game is uniq in the whole document
	 * 
	 * @param mPGL
	 */
	@Check(CheckType.FAST)
	public void checkMPGLUniq(mPGL mPGL) {
		try {
			clearVariables();
			this.checkUniq(mPGL, mPGL, mPGL.getName());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void clearVariables() {
		this.errorFound = false;
		this.errorMessage = "";
	}

	/**
	 * an other example for the game attr list elements
	 * 
	 * @param gameAttrAss
	 */
	@Check(CheckType.FAST)
	public void checkMPGLAttrUniq(GameAttrAss gameAttrAss) {
		try {
			clearVariables();
			mPGL mPGL = (mPGL) gameAttrAss.eContainer().eContainer();
			this.checkUniq(mPGL, gameAttrAss, gameAttrAss.getName());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * helper method: check the whole tree against a name and looks only at the
	 * object that equal not the 'checkObject'
	 * 
	 * @param mPGL
	 *            the parent object where the search beginns
	 * @param checkObject
	 *            the object that don't need to check
	 * @param name
	 *            the name to search
	 */
	private void checkUniq(mPGL mPGL, EObject checkObject, String name) {
		// TODO: testen, wegen refactoring nicht mehr 100%ig richtig
		// Problem liegt bei den VarDecl und ObjDecl

		try {
			if (mPGL != checkObject && mPGL.getName().equals(name)) {
				this.errorMessage += "duplicate found with mPGL. ";
				errorFound = true;
			}

			this.checkGameAttributes(mPGL.getAttrAssList().getAttr(),
					checkObject, name);

			Iterator<Decl> it = mPGL.getDecl().iterator();
			while (it.hasNext()) {
				Decl aDecl = it.next();
				if (aDecl != null) {
					if (aDecl.getVarDecl() != null
							&& aDecl.getVarDecl() != checkObject) {
						if (aDecl.getVarDecl().getName().equals(name)) {
							errorMessage += "duplicate found at a VarDecl. ";
							errorFound = true;
						}
					} else if (aDecl.getObjDecl() != null
							&& aDecl.getObjDecl() != checkObject) {
						// TODO: Type-check and cast for all obj (array) decl's
					}
				}
			}

			Iterator<Block> blockIt = mPGL.getBlock().iterator();
			while (blockIt.hasNext()) {
				Block block = (Block) blockIt.next();
				if (block.getAnimBlock() != null
						&& block.getAnimBlock() != checkObject
						&& block.getAnimBlock().getName() != null
						&& block.getAnimBlock().getName().equals(name)) {
					errorMessage += "duplicate found at an AnimBlock. ";
					errorFound = true;
				}
			}
			if (errorFound) {
				error(errorMessage, MPGLPackage.MPGL__NAME);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * search the attributes of the game
	 * 
	 * @param list
	 *            the game attribute list
	 * @param checkObject
	 *            the object not to look at
	 * @param name
	 *            the name of the searched element
	 */
	private void checkGameAttributes(EList<GameAttrAss> list,
			EObject checkObject, String name) {
		Iterator<GameAttrAss> attrListIt = list.iterator();

		while (attrListIt.hasNext()) {
			GameAttrAss attrAss = attrListIt.next();
			if (attrAss != checkObject && attrAss.getName().equals(name)) {
				errorMessage += "duplicate found at an game attr. ";
				errorFound = true;
			}

		}
	}

	/**
	 * search the attributes
	 * 
	 * @param list
	 *            the game attribute list
	 * @param parentObject
	 *            the object not to look at
	 * @param name
	 *            the name of the searched element
	 */
	// TODO: Reprogramm
	// private void checkAttributes(EList<AttrAss> list, EObject checkObject,
	// String name) {
	// Iterator<AttrAss> attrListIt = list.iterator();
	//
	// while (attrListIt.hasNext()) {
	// AttrAss attrAss = attrListIt.next();
	//
	// // TODO: Check against new grammar
	//
	// // if (attrAss != checkObject && attrAss.getName().equals(name)) {
	// // errorMessage += "duplicate found at an object attr. ";
	// // errorFound = true;
	// // }
	//
	// }
	// }
}
